package com.atguigu.gmall.search.service.impl;

import com.atguigu.gmall.search.vo.SearchRespVo.OrderMap;
import com.google.common.collect.Lists;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.search.bean.Goods;
import com.atguigu.gmall.search.repo.GoodsRepository;
import com.atguigu.gmall.search.service.SearchService;
import com.atguigu.gmall.search.vo.SearchParamVo;
import com.atguigu.gmall.search.vo.SearchRespVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.mapper.ParseContext;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.profile.aggregation.AggregationTimingType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.Query.*;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.data.elasticsearch.core.document.Document;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public void up(Goods goods) {

        goodsRepository.save(goods);
    }

    @Override
    public void down(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    int pageSize = 10;

    @Override
    public SearchRespVo search(SearchParamVo param) {
        SearchRespVo searchRespVo = new SearchRespVo();
        //根据索引查询
        Query query = buildQuery(param);

        SearchHits<Goods> result = elasticsearchRestTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));

        SearchRespVo respVo = buildSearchResp(result, param);
        return respVo;
    }

    /**
     * 更新热度
     * @param skuId
     * @param increment
     */
    @Override
    public void updateHotScore(Long skuId, Long increment) {
        Document document = Document.create();
        document.put("hotScore",increment);
        UpdateQuery updateQuery = UpdateQuery.builder("" + skuId)
                .withDocAsUpsert(true)
                .withDocument(document).build();
        elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("goods"));
    }

    /**
     * 返回结果
     *
     * @param result
     * @param param
     * @return
     */
    private SearchRespVo buildSearchResp(SearchHits<Goods> result, SearchParamVo param) {

        SearchRespVo searchRespVo = new SearchRespVo();

        searchRespVo.setSearchParam(param);
        //品牌面包屑
        searchRespVo.setTrademarkParam(param.getTrademark());
        //属性面包屑List

        if (param.getProps() != null && param.getProps().length > 0) {
            List<SearchRespVo.Props> propsList = Arrays.stream(param.getProps()).map(item -> {
                String[] split = item.split(":");
                SearchRespVo.Props prop = new SearchRespVo.Props();

                prop.setAttrName(split[2]);
                prop.setAttrValue(split[1]);
                prop.setAttrId(Long.parseLong(split[0]));
                return prop;
            }).collect(Collectors.toList());


            searchRespVo.setPropsParamList(propsList);
        }

        //品牌列表

        ParsedLongTerms tmIdAgg = result.getAggregations().get("tmIdAgg");

        List<SearchRespVo.Trademark> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchRespVo.Trademark trademark = new SearchRespVo.Trademark();
            Long tmid = bucket.getKeyAsNumber().longValue();
            trademark.setTmId(tmid);
            //获得子桶
            ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            trademark.setTmName(tmName);
            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String LogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            trademark.setTmLogoUrl(LogoUrl);
            return trademark;

        }).collect(Collectors.toList());
        searchRespVo.setTrademarkList(trademarkList);


        //属性列表
        ParsedNested attrAgg = result.getAggregations().get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");

        List<SearchRespVo.Attrs> attrsList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchRespVo.Attrs attrs = new SearchRespVo.Attrs();
            //获取属性id
            long attrId = bucket.getKeyAsNumber().longValue();
            attrs.setAttrId(attrId);
            //获取属性id子桶 attrNameAgg
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            //获取属性名称
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();

            attrs.setAttrName(attrName);

            //获取属性值
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> stringList = attrValueAgg.getBuckets().stream().map(item ->
                    item.getKeyAsString()
            ).collect(Collectors.toList());
            attrs.setAttrValueList(stringList);

            return attrs;
        }).collect(Collectors.toList());

        searchRespVo.setAttrsList(attrsList);

       /* ParsedNested attrAgg = result.getAggregations().get("attrAgg");
        //属性id聚合结果
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<SearchRespVo.Attrs> attrsList = attrIdAgg.getBuckets()
                .stream()
                .map(bucket -> {
                    SearchRespVo.Attrs attrs = new SearchRespVo.Attrs();
                    //属性id
                    long attrId = bucket.getKeyAsNumber().longValue();
                    attrs.setAttrId(attrId);
                    //属性名
                    ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                    String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                    attrs.setAttrName(attrName);

                    //属性值
                    ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
                    List<String> valueList = attrValueAgg.getBuckets()
                            .stream()
                            .map(item -> item.getKeyAsString())
                            .collect(Collectors.toList());
                    attrs.setAttrValueList(valueList);
                    return attrs;
                })
                .collect(Collectors.toList());

        searchRespVo.setAttrsList(attrsList);*/


        //6、url参数 list.html?category3Id=61&trademark=2:华为&props=4:256GB:机身存储&props=3:8GB:运行内存
        String urlParam = buildUrlParam(param);
        searchRespVo.setUrlParam(urlParam);

        //7、排序 1/2 : Asc/Desc
        if (param.getOrder() != null && param.getOrder().length() > 0) {
            OrderMap OrderMap = new OrderMap();
            String order = param.getOrder();
            String[] split = order.split(":");
            OrderMap.setSort(split[1]);
            OrderMap.setType(split[0]);

            searchRespVo.setOrderMap(OrderMap);
        }

        //8、商品集合;
        if (result.getSearchHits() != null && result.getSearchHits().size() > 0) {
            List<SearchHit<Goods>> searchHits = result.getSearchHits();
            List<Goods> goods = searchHits.stream().map(item -> {
                Goods content = item.getContent();

                if (!StringUtils.isEmpty(param.getKeyword())) {
                    String title = item.getHighlightField("title").get(0);
                    content.setTitle(title);
                }
                return content;
            }).collect(Collectors.toList());
            searchRespVo.setGoodsList(goods);
        }

        //9、页码
        Integer pageNo = param.getPageNo();
        searchRespVo.setPageNo(pageNo);
        //总页码： 总记录数%每页大小==0？总记录数/每页大小:总记录数/每页大小 + 1
        long totalHits = result.getTotalHits();
        searchRespVo.setTotalPages(totalHits % pageSize == 0 ? totalHits / pageSize : totalHits / pageSize + 1);


        return searchRespVo;

    }

    /**
     * 创建Url
     *
     * @param searchParamVo
     * @return
     */
    private String buildUrlParam(SearchParamVo searchParamVo) {
        StringBuilder builder = new StringBuilder("list.html?");
        //分类参数
        if (searchParamVo.getCategory1Id() != null) {
            builder.append("&category1Id=" + searchParamVo.getCategory1Id());
        }
        if (searchParamVo.getCategory2Id() != null) {
            builder.append("&category2Id=" + searchParamVo.getCategory2Id());
        }
        if (searchParamVo.getCategory3Id() != null) {
            builder.append("&category3Id=" + searchParamVo.getCategory3Id());
        }

        //keyword
        if (!StringUtils.isEmpty(searchParamVo.getKeyword())) {
            builder.append("&keyword=" + searchParamVo.getKeyword());
        }

        //品牌
        if (!StringUtils.isEmpty(searchParamVo.getTrademark())) {
            builder.append("&trademark=" + searchParamVo.getTrademark());
        }

        //属性
        if (searchParamVo.getProps() != null && searchParamVo.getProps().length > 0) {
            Arrays.stream(searchParamVo.getProps()).forEach(item -> {
                builder.append("&props=" + item);
            });
        }


        return builder.toString();
    }

    /**
     * 创建查询
     *
     * @param param
     * @return
     */
    private Query buildQuery(SearchParamVo param) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //一级分类
        if (param.getCategory1Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category1Id", param.getCategory1Id()));
        }
        //二级分类
        if (param.getCategory2Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category2Id", param.getCategory2Id()));
        }
        //三级分类
        if (param.getCategory3Id() != null) {
            boolQuery.must(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));
        }
        //关键字查询
        String keyword = param.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            boolQuery.must(QueryBuilders.matchQuery("title", param.getKeyword()));
        }
        //品牌查询
        if (!StringUtils.isEmpty(param.getTrademark())) {
            String[] split = param.getTrademark().split(":");
            TermQueryBuilder tmId = QueryBuilders.termQuery("tmId", split[0]);
            boolQuery.must(tmId);
        }
        //属性查询
        if (param.getProps() != null && param.getProps().length > 0) {
            //遍历
            Arrays.stream(param.getProps()).forEach((item) -> {
                //截串： 4:256GB:机身存储
                String[] split = item.split(":");

                BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrName", split[1]));

                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None);

                boolQuery.must(nestedQuery);
            });
        }

        NativeSearchQuery query = new NativeSearchQuery(boolQuery);
        //排序  1/2 : DESC/ASC
        Sort sort = null;
        String order = param.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            Sort.Direction direction = "asc".equals(split[1]) ? Sort.Direction.ASC : Sort.Direction.DESC;

            switch (split[0]) {
                case "1":
                    sort = Sort.by(direction, "hotScore");
                    break;
                case "2":
                    sort = Sort.by(direction, "price");
                    break;
                default:
                    sort = Sort.by(Sort.Direction.DESC, "hotScore");
            }

            query.addSort(sort);

        }
        //分页
        Integer pageNo = param.getPageNo();

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);

        query.setPageable(pageable);

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        highlightBuilder.field("title");
        HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
        query.setHighlightQuery(highlightQuery);

        //聚合

        //品牌聚合
        //先创建id聚合
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg").field("tmId").size(200);

        //品牌子聚合
        //在创建id聚合的子聚合name聚合 + LogoUrl聚合
        TermsAggregationBuilder tmNameAgg = AggregationBuilders.terms("tmNameAgg").field("tmName").size(1);
        TermsAggregationBuilder tmValueAgg = AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(200);
        tmIdAgg.subAggregation(tmNameAgg);
        tmIdAgg.subAggregation(tmValueAgg);
        //将id聚合添加到query中
        query.addAggregation(tmIdAgg);


        //属性聚合  属性id 属性名 属性值

        //先创建属性id聚合
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrAgg", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(200);

        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(200);
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(200);
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);
        attrAgg.subAggregation(attrIdAgg);
/*
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrAgg", "attrs");

        //属性id聚合分析
        TermsAggregationBuilder attrIdAgg = AggregationBuilders
                .terms("attrIdAgg")
                .field("attrs.attrId")
                .size(200);
        //属性名聚合分析
        TermsAggregationBuilder attrNameAgg = AggregationBuilders
                .terms("attrNameAgg")
                .field("attrs.attrName")
                .size(1);
        attrIdAgg.subAggregation(attrNameAgg);

        //属性值聚合分析
        TermsAggregationBuilder attrValueAgg = AggregationBuilders
                .terms("attrValueAgg")
                .field("attrs.attrValue")
                .size(100);
        attrIdAgg.subAggregation(attrValueAgg);

        attrAgg.subAggregation(attrIdAgg);*/
        query.addAggregation(attrAgg);

//        //将属性id聚合添加到query中
//        query.addAggregation(attrAgg);

        return query;

    }


}
